/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

// Created on 18.01.2004 by RST.
// $Id: SV_CCMDS.java,v 1.15 2005/12/03 19:45:42 salomo Exp $
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Cmd = Suake2.UI.game.Cmd;
using EndianHandler = Suake2.UI.game.EndianHandler;
using GameSVCmds = Suake2.UI.game.GameSVCmds;
using GameSave = Suake2.UI.game.GameSave;
using Info = Suake2.UI.game.Info;
using cvar_t = Suake2.UI.game.cvar_t;
using CM = Suake2.UI.qcommon.CM;
using Com = Suake2.UI.qcommon.Com;
using Cvar = Suake2.UI.qcommon.Cvar;
using FS = Suake2.UI.qcommon.FS;
using MSG = Suake2.UI.qcommon.MSG;
using Netchan = Suake2.UI.qcommon.Netchan;
using SZ = Suake2.UI.qcommon.SZ;
using netadr_t = Suake2.UI.qcommon.netadr_t;
using sizebuf_t = Suake2.UI.qcommon.sizebuf_t;
using xcommand_t = Suake2.UI.qcommon.xcommand_t;
using NET = Suake2.UI.sys.NET;
using Sys = Suake2.UI.sys.Sys;
using Lib = Suake2.UI.util.Lib;
using QuakeFile = Suake2.UI.util.QuakeFile;
using Vargs = Suake2.UI.util.Vargs;
namespace Suake2.UI.server
{
	
	public class SV_CCMDS
	{
		private class AnonymousClassxcommand_t:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_Heartbeat_f();
			}
		}
		private class AnonymousClassxcommand_t1:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_Kick_f();
			}
		}
		private class AnonymousClassxcommand_t2:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_Status_f();
			}
		}
		private class AnonymousClassxcommand_t3:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_Serverinfo_f();
			}
		}
		private class AnonymousClassxcommand_t4:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_DumpUser_f();
			}
		}
		private class AnonymousClassxcommand_t5:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_Map_f();
			}
		}
		private class AnonymousClassxcommand_t6:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_DemoMap_f();
			}
		}
		private class AnonymousClassxcommand_t7:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_GameMap_f();
			}
		}
		private class AnonymousClassxcommand_t8:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_SetMaster_f();
			}
		}
		private class AnonymousClassxcommand_t9:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_ConSay_f();
			}
		}
		private class AnonymousClassxcommand_t10:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_ServerRecord_f();
			}
		}
		private class AnonymousClassxcommand_t11:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_ServerStop_f();
			}
		}
		private class AnonymousClassxcommand_t12:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_Savegame_f();
			}
		}
		private class AnonymousClassxcommand_t13:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_Loadgame_f();
			}
		}
		private class AnonymousClassxcommand_t14:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_KillServer_f();
			}
		}
		private class AnonymousClassxcommand_t15:xcommand_t
		{
			public override void  execute()
			{
				jake2.server.SV_CCMDS.SV_ServerCommand_f();
			}
		}
		
		/*
		===============================================================================
		
		OPERATOR CONSOLE ONLY COMMANDS
		
		These commands can only be entered from stdin or by a remote operator datagram
		===============================================================================
		*/
		
		/*
		====================
		SV_SetMaster_f
		
		Specify a list of master servers
		====================
		*/
		public static void  SV_SetMaster_f()
		{
			int i, slot;
			
			// only dedicated servers send heartbeats
			if (Globals.dedicated.value_Renamed == 0)
			{
				Com.Printf("Only dedicated servers use masters.\n");
				return ;
			}
			
			// make sure the server is listed public
			Cvar.Set("public", "1");
			
			for (i = 1; i < Defines.MAX_MASTERS; i++)
				SV_MAIN.master_adr[i] = new netadr_t();
			
			slot = 1; // slot 0 will always contain the id master
			for (i = 1; i < Cmd.Argc(); i++)
			{
				if (slot == Defines.MAX_MASTERS)
					break;
				
				if (!NET.StringToAdr(Cmd.Argv(i), SV_MAIN.master_adr[i]))
				{
					Com.Printf("Bad address: " + Cmd.Argv(i) + "\n");
					continue;
				}
				if (SV_MAIN.master_adr[slot].port == 0)
					SV_MAIN.master_adr[slot].port = Defines.PORT_MASTER;
				
				Com.Printf("Master server at " + NET.AdrToString(SV_MAIN.master_adr[slot]) + "\n");
				Com.Printf("Sending a ping.\n");
				
				Netchan.OutOfBandPrint(Defines.NS_SERVER, SV_MAIN.master_adr[slot], "ping");
				
				slot++;
			}
			
			SV_INIT.svs.last_heartbeat = - 9999999;
		}
		/*
		==================
		SV_SetPlayer
		
		Sets sv_client and sv_player to the player with idnum Cmd.Argv(1)
		==================
		*/
		public static bool SV_SetPlayer()
		{
			client_t cl;
			int i;
			int idnum;
			System.String s;
			
			if (Cmd.Argc() < 2)
				return false;
			
			s = Cmd.Argv(1);
			
			// numeric values are just slot numbers
			if (s[0] >= '0' && s[0] <= '9')
			{
				idnum = Lib.atoi(Cmd.Argv(1));
				if (idnum < 0 || idnum >= SV_MAIN.maxclients.value_Renamed)
				{
					Com.Printf("Bad client slot: " + idnum + "\n");
					return false;
				}
				
				SV_MAIN.sv_client = SV_INIT.svs.clients[idnum];
				SV_USER.sv_player = SV_MAIN.sv_client.edict;
				if (0 == SV_MAIN.sv_client.state)
				{
					Com.Printf("Client " + idnum + " is not active\n");
					return false;
				}
				return true;
			}
			
			// check for a name match
			for (i = 0; i < SV_MAIN.maxclients.value_Renamed; i++)
			{
				cl = SV_INIT.svs.clients[i];
				if (0 == cl.state)
					continue;
				if (0 == Lib.strcmp(cl.name, s))
				{
					SV_MAIN.sv_client = cl;
					SV_USER.sv_player = SV_MAIN.sv_client.edict;
					return true;
				}
			}
			
			Com.Printf("Userid " + s + " is not on the server\n");
			return false;
		}
		/*
		===============================================================================
		
		SAVEGAME FILES
		
		===============================================================================
		*/
		
		public static void  remove(System.String name)
		{
			try
			{
				bool tmpBool;
				if (System.IO.File.Exists(new System.IO.FileInfo(name).FullName))
				{
					System.IO.File.Delete(new System.IO.FileInfo(name).FullName);
					tmpBool = true;
				}
				else if (System.IO.Directory.Exists(new System.IO.FileInfo(name).FullName))
				{
					System.IO.Directory.Delete(new System.IO.FileInfo(name).FullName);
					tmpBool = true;
				}
				else
					tmpBool = false;
				bool generatedAux = tmpBool;
			}
			catch (System.Exception e)
			{
			}
		}
		
		/// <summary>Delete save files save/(number)/.  </summary>
		public static void  SV_WipeSavegame(System.String savename)
		{
			
			System.String name, s;
			
			Com.DPrintf("SV_WipeSaveGame(" + savename + ")\n");
			
			name = FS.Gamedir() + "/save/" + savename + "/server.ssv";
			remove(name);
			
			name = FS.Gamedir() + "/save/" + savename + "/game.ssv";
			remove(name);
			
			name = FS.Gamedir() + "/save/" + savename + "/*.sav";
			
			System.IO.FileInfo f = Sys.FindFirst(name, 0, 0);
			while (f != null)
			{
				bool tmpBool;
				if (System.IO.File.Exists(f.FullName))
				{
					System.IO.File.Delete(f.FullName);
					tmpBool = true;
				}
				else if (System.IO.Directory.Exists(f.FullName))
				{
					System.IO.Directory.Delete(f.FullName);
					tmpBool = true;
				}
				else
					tmpBool = false;
				bool generatedAux = tmpBool;
				f = Sys.FindNext();
			}
			Sys.FindClose();
			
			name = FS.Gamedir() + "/save/" + savename + "/*.sv2";
			
			f = Sys.FindFirst(name, 0, 0);
			
			while (f != null)
			{
				bool tmpBool2;
				if (System.IO.File.Exists(f.FullName))
				{
					System.IO.File.Delete(f.FullName);
					tmpBool2 = true;
				}
				else if (System.IO.Directory.Exists(f.FullName))
				{
					System.IO.Directory.Delete(f.FullName);
					tmpBool2 = true;
				}
				else
					tmpBool2 = false;
				bool generatedAux2 = tmpBool2;
				f = Sys.FindNext();
			}
			Sys.FindClose();
		}
		/*
		================
		CopyFile
		================
		*/
		public static void  CopyFile(System.String src, System.String dst)
		{
			//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
			System.IO.FileStream f1, f2;
			int l = - 1;
			sbyte[] buffer = new sbyte[65536];
			
			//Com.DPrintf("CopyFile (" + src + ", " + dst + ")\n");
			try
			{
				f1 = SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(src, "r");
			}
			catch (System.Exception e)
			{
				return ;
			}
			try
			{
				f2 = SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(dst, "rw");
			}
			catch (System.Exception e)
			{
				try
				{
					f1.Close();
				}
				catch (System.IO.IOException e1)
				{
					SupportClass.WriteStackTrace(e1, Console.Error);
				}
				return ;
			}
			
			while (true)
			{
				
				try
				{
					l = SupportClass.ReadInput(f1, buffer, 0, 65536);
				}
				catch (System.IO.IOException e1)
				{
					
					SupportClass.WriteStackTrace(e1, Console.Error);
				}
				if (l == - 1)
					break;
				try
				{
					f2.Write(SupportClass.ToByteArray(buffer), 0, l);
				}
				catch (System.IO.IOException e2)
				{
					
					SupportClass.WriteStackTrace(e2, Console.Error);
				}
			}
			
			try
			{
				f1.Close();
			}
			catch (System.IO.IOException e1)
			{
				
				SupportClass.WriteStackTrace(e1, Console.Error);
			}
			try
			{
				f2.Close();
			}
			catch (System.IO.IOException e2)
			{
				
				SupportClass.WriteStackTrace(e2, Console.Error);
			}
		}
		/*
		================
		SV_CopySaveGame
		================
		*/
		public static void  SV_CopySaveGame(System.String src, System.String dst)
		{
			//char name[MAX_OSPATH], name2[MAX_OSPATH];
			int l, len;
			System.IO.FileInfo found;
			
			System.String name, name2;
			
			Com.DPrintf("SV_CopySaveGame(" + src + "," + dst + ")\n");
			
			SV_WipeSavegame(dst);
			
			// copy the savegame over
			name = FS.Gamedir() + "/save/" + src + "/server.ssv";
			name2 = FS.Gamedir() + "/save/" + dst + "/server.ssv";
			FS.CreatePath(name2);
			CopyFile(name, name2);
			
			name = FS.Gamedir() + "/save/" + src + "/game.ssv";
			name2 = FS.Gamedir() + "/save/" + dst + "/game.ssv";
			CopyFile(name, name2);
			
			System.String name1 = FS.Gamedir() + "/save/" + src + "/";
			len = name1.Length;
			name = FS.Gamedir() + "/save/" + src + "/*.sav";
			
			found = Sys.FindFirst(name, 0, 0);
			
			while (found != null)
			{
				name = name1 + found.Name;
				name2 = FS.Gamedir() + "/save/" + dst + "/" + found.Name;
				
				CopyFile(name, name2);
				
				// change sav to sv2
				name = name.Substring(0, (name.Length - 3) - (0)) + "sv2";
				name2 = name2.Substring(0, (name2.Length - 3) - (0)) + "sv2";
				
				CopyFile(name, name2);
				
				found = Sys.FindNext();
			}
			Sys.FindClose();
		}
		/*
		==============
		SV_WriteLevelFile
		
		==============
		*/
		public static void  SV_WriteLevelFile()
		{
			
			System.String name;
			QuakeFile f;
			
			Com.DPrintf("SV_WriteLevelFile()\n");
			
			name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sv2";
			
			try
			{
				f = new QuakeFile(name, "rw");
				
				for (int i = 0; i < Defines.MAX_CONFIGSTRINGS; i++)
					f.writeString(SV_INIT.sv.configstrings[i]);
				
				CM.CM_WritePortalState(f);
				f.Close();
			}
			catch (System.Exception e)
			{
				Com.Printf("Failed to open " + name + "\n");
				SupportClass.WriteStackTrace(e, Console.Error);
			}
			
			name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sav";
			GameSave.WriteLevel(name);
		}
		/*
		==============
		SV_ReadLevelFile
		
		==============
		*/
		public static void  SV_ReadLevelFile()
		{
			//char name[MAX_OSPATH];
			System.String name;
			QuakeFile f;
			
			Com.DPrintf("SV_ReadLevelFile()\n");
			
			name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sv2";
			try
			{
				f = new QuakeFile(name, "r");
				
				for (int n = 0; n < Defines.MAX_CONFIGSTRINGS; n++)
					SV_INIT.sv.configstrings[n] = f.readString();
				
				CM.CM_ReadPortalState(f);
				
				f.Close();
			}
			catch (System.IO.IOException e1)
			{
				Com.Printf("Failed to open " + name + "\n");
				SupportClass.WriteStackTrace(e1, Console.Error);
			}
			
			name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sav";
			GameSave.ReadLevel(name);
		}
		/*
		==============
		SV_WriteServerFile
		
		==============
		*/
		public static void  SV_WriteServerFile(bool autosave)
		{
			QuakeFile f;
			cvar_t var;
			
			System.String filename, name, string_Renamed, comment;
			
			Com.DPrintf("SV_WriteServerFile(" + (autosave?"true":"false") + ")\n");
			
			filename = FS.Gamedir() + "/save/current/server.ssv";
			try
			{
				f = new QuakeFile(filename, "rw");
				
				if (!autosave)
				{
					System.Globalization.Calendar c = new System.Globalization.GregorianCalendar();
					//UPGRADE_TODO: Method 'java.util.Calendar.get' was converted to 'SupportClass.CalendarManager.Get' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilCalendarget_int'"
					comment = Com.sprintf("%2i:%2i %2i/%2i  ", new Vargs().add(SupportClass.CalendarManager.manager.Get(c, SupportClass.CalendarManager.HOUR_OF_DAY)).add(SupportClass.CalendarManager.manager.Get(c, SupportClass.CalendarManager.MINUTE)).add(SupportClass.CalendarManager.manager.Get(c, SupportClass.CalendarManager.MONTH) + 1).add(SupportClass.CalendarManager.manager.Get(c, SupportClass.CalendarManager.DAY_OF_MONTH)));
					comment += SV_INIT.sv.configstrings[Defines.CS_NAME];
				}
				else
				{
					// autosaved
					comment = "ENTERING " + SV_INIT.sv.configstrings[Defines.CS_NAME];
				}
				
				f.writeString(comment);
				f.writeString(SV_INIT.svs.mapcmd);
				
				// write the mapcmd
				
				// write all CVAR_LATCH cvars
				// these will be things like coop, skill, deathmatch, etc
				for (var = Globals.cvar_vars; var != null; var = var.next)
				{
					if (0 == (var.flags & Defines.CVAR_LATCH))
						continue;
					if (var.name.Length >= Defines.MAX_OSPATH - 1 || var.string_Renamed.Length >= 128 - 1)
					{
						Com.Printf("Cvar too long: " + var.name + " = " + var.string_Renamed + "\n");
						continue;
					}
					
					name = var.name;
					string_Renamed = var.string_Renamed;
					try
					{
						f.writeString(name);
						f.writeString(string_Renamed);
					}
					catch (System.IO.IOException e2)
					{
					}
				}
				// rst: for termination.
				f.writeString(null);
				f.Close();
			}
			catch (System.Exception e)
			{
				Com.Printf("Couldn't write " + filename + "\n");
			}
			
			// write game state
			filename = FS.Gamedir() + "/save/current/game.ssv";
			GameSave.WriteGame(filename, autosave);
		}
		/*
		==============
		SV_ReadServerFile
		
		==============
		*/
		public static void  SV_ReadServerFile()
		{
			System.String filename = "", name = "", string_Renamed, comment, mapcmd;
			try
			{
				QuakeFile f;
				
				mapcmd = "";
				
				Com.DPrintf("SV_ReadServerFile()\n");
				
				filename = FS.Gamedir() + "/save/current/server.ssv";
				
				f = new QuakeFile(filename, "r");
				
				// read the comment field
				comment = f.readString();
				
				// read the mapcmd
				mapcmd = f.readString();
				
				// read all CVAR_LATCH cvars
				// these will be things like coop, skill, deathmatch, etc
				while (true)
				{
					name = f.readString();
					if (name == null)
						break;
					string_Renamed = f.readString();
					
					Com.DPrintf("Set " + name + " = " + string_Renamed + "\n");
					Cvar.ForceSet(name, string_Renamed);
				}
				
				f.Close();
				
				// start a new game fresh with new cvars
				SV_INIT.SV_InitGame();
				
				SV_INIT.svs.mapcmd = mapcmd;
				
				// read game state
				filename = FS.Gamedir() + "/save/current/game.ssv";
				GameSave.ReadGame(filename);
			}
			catch (System.Exception e)
			{
				Com.Printf("Couldn't read file " + filename + "\n");
				SupportClass.WriteStackTrace(e, Console.Error);
			}
		}
		//=========================================================
		
		/*
		==================
		SV_DemoMap_f
		
		Puts the server in demo mode on a specific map/cinematic
		==================
		*/
		public static void  SV_DemoMap_f()
		{
			SV_INIT.SV_Map(true, Cmd.Argv(1), false);
		}
		/*
		==================
		SV_GameMap_f
		
		Saves the state of the map just being exited and goes to a new map.
		
		If the initial character of the map string is '*', the next map is
		in a new unit, so the current savegame directory is cleared of
		map files.
		
		Example:
		
		*inter.cin+jail
		
		Clears the archived maps, plays the inter.cin cinematic, then
		goes to map jail.bsp.
		==================
		*/
		public static void  SV_GameMap_f()
		{
			System.String map;
			int i;
			client_t cl;
			bool[] savedInuse;
			
			if (Cmd.Argc() != 2)
			{
				Com.Printf("USAGE: gamemap <map>\n");
				return ;
			}
			
			Com.DPrintf("SV_GameMap(" + Cmd.Argv(1) + ")\n");
			
			FS.CreatePath(FS.Gamedir() + "/save/current/");
			
			// check for clearing the current savegame
			map = Cmd.Argv(1);
			if (map[0] == '*')
			{
				// wipe all the *.sav files
				SV_WipeSavegame("current");
			}
			else
			{
				// save the map just exited
				if (SV_INIT.sv.state == Defines.ss_game)
				{
					// clear all the client inuse flags before saving so that
					// when the level is re-entered, the clients will spawn
					// at spawn points instead of occupying body shells
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					savedInuse = new bool[(int) SV_MAIN.maxclients.value_Renamed];
					for (i = 0; i < SV_MAIN.maxclients.value_Renamed; i++)
					{
						cl = SV_INIT.svs.clients[i];
						savedInuse[i] = cl.edict.inuse;
						cl.edict.inuse = false;
					}
					
					SV_WriteLevelFile();
					
					// we must restore these for clients to transfer over correctly
					for (i = 0; i < SV_MAIN.maxclients.value_Renamed; i++)
					{
						cl = SV_INIT.svs.clients[i];
						cl.edict.inuse = savedInuse[i];
					}
					savedInuse = null;
				}
			}
			
			// start up the next map
			SV_INIT.SV_Map(false, Cmd.Argv(1), false);
			
			// archive server state
			SV_INIT.svs.mapcmd = Cmd.Argv(1);
			
			// copy off the level to the autosave slot
			if (0 == Globals.dedicated.value_Renamed)
			{
				SV_WriteServerFile(true);
				SV_CopySaveGame("current", "save0");
			}
		}
		/*
		==================
		SV_Map_f
		
		Goes directly to a given map without any savegame archiving.
		For development work
		==================
		*/
		public static void  SV_Map_f()
		{
			System.String map;
			//char expanded[MAX_QPATH];
			System.String expanded;
			
			// if not a pcx, demo, or cinematic, check to make sure the level exists
			map = Cmd.Argv(1);
			if (map.IndexOf(".") < 0)
			{
				expanded = "maps/" + map + ".bsp";
				if (FS.LoadFile(expanded) == null)
				{
					
					Com.Printf("Can't find " + expanded + "\n");
					return ;
				}
			}
			
			SV_INIT.sv.state = Defines.ss_dead; // don't save current level when changing
			
			SV_WipeSavegame("current");
			SV_GameMap_f();
		}
		/*
		=====================================================================
		
		SAVEGAMES
		
		=====================================================================
		*/
		
		/*
		==============
		SV_Loadgame_f
		
		==============
		*/
		public static void  SV_Loadgame_f()
		{
			
			System.String name;
			//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
			System.IO.FileStream f;
			System.String dir;
			
			if (Cmd.Argc() != 2)
			{
				Com.Printf("USAGE: loadgame <directory>\n");
				return ;
			}
			
			Com.Printf("Loading game...\n");
			
			dir = Cmd.Argv(1);
			if ((dir.IndexOf("..") > - 1) || (dir.IndexOf("/") > - 1) || (dir.IndexOf("\\") > - 1))
			{
				Com.Printf("Bad savedir.\n");
			}
			
			// make sure the server.ssv file exists
			name = FS.Gamedir() + "/save/" + Cmd.Argv(1) + "/server.ssv";
			try
			{
				f = SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(name, "r");
			}
			catch (System.IO.FileNotFoundException e)
			{
				Com.Printf("No such savegame: " + name + "\n");
				return ;
			}
			
			try
			{
				f.Close();
			}
			catch (System.IO.IOException e1)
			{
				SupportClass.WriteStackTrace(e1, Console.Error);
			}
			
			SV_CopySaveGame(Cmd.Argv(1), "current");
			SV_ReadServerFile();
			
			// go to the map
			SV_INIT.sv.state = Defines.ss_dead; // don't save current level when changing
			SV_INIT.SV_Map(false, SV_INIT.svs.mapcmd, true);
		}
		/*
		==============
		SV_Savegame_f
		
		==============
		*/
		public static void  SV_Savegame_f()
		{
			System.String dir;
			
			if (SV_INIT.sv.state != Defines.ss_game)
			{
				Com.Printf("You must be in a game to save.\n");
				return ;
			}
			
			if (Cmd.Argc() != 2)
			{
				Com.Printf("USAGE: savegame <directory>\n");
				return ;
			}
			
			if (Cvar.VariableValue("deathmatch") != 0)
			{
				Com.Printf("Can't savegame in a deathmatch\n");
				return ;
			}
			
			if (0 == Lib.strcmp(Cmd.Argv(1), "current"))
			{
				Com.Printf("Can't save to 'current'\n");
				return ;
			}
			
			if (SV_MAIN.maxclients.value_Renamed == 1 && SV_INIT.svs.clients[0].edict.client.ps.stats[Defines.STAT_HEALTH] <= 0)
			{
				Com.Printf("\nCan't savegame while dead!\n");
				return ;
			}
			
			dir = Cmd.Argv(1);
			if ((dir.IndexOf("..") > - 1) || (dir.IndexOf("/") > - 1) || (dir.IndexOf("\\") > - 1))
			{
				Com.Printf("Bad savedir.\n");
			}
			
			Com.Printf("Saving game...\n");
			
			// archive current level, including all client edicts.
			// when the level is reloaded, they will be shells awaiting
			// a connecting client
			SV_WriteLevelFile();
			
			// save server state
			try
			{
				SV_WriteServerFile(false);
			}
			catch (System.Exception e)
			{
				//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				Com.Printf("IOError in SV_WriteServerFile: " + e);
			}
			
			// copy it off
			SV_CopySaveGame("current", dir);
			Com.Printf("Done.\n");
		}
		//===============================================================
		/*
		==================
		SV_Kick_f
		
		Kick a user off of the server
		==================
		*/
		public static void  SV_Kick_f()
		{
			if (!SV_INIT.svs.initialized)
			{
				Com.Printf("No server running.\n");
				return ;
			}
			
			if (Cmd.Argc() != 2)
			{
				Com.Printf("Usage: kick <userid>\n");
				return ;
			}
			
			if (!SV_SetPlayer())
				return ;
			
			SV_SEND.SV_BroadcastPrintf(Defines.PRINT_HIGH, SV_MAIN.sv_client.name + " was kicked\n");
			// print directly, because the dropped client won't get the
			// SV_BroadcastPrintf message
			SV_SEND.SV_ClientPrintf(SV_MAIN.sv_client, Defines.PRINT_HIGH, "You were kicked from the game\n");
			SV_MAIN.SV_DropClient(SV_MAIN.sv_client);
			SV_MAIN.sv_client.lastmessage = SV_INIT.svs.realtime; // min case there is a funny zombie
		}
		/*
		================
		SV_Status_f
		================
		*/
		public static void  SV_Status_f()
		{
			int i, j, l;
			client_t cl;
			System.String s;
			int ping;
			if (SV_INIT.svs.clients == null)
			{
				Com.Printf("No server running.\n");
				return ;
			}
			Com.Printf("map              : " + SV_INIT.sv.name + "\n");
			
			Com.Printf("num score ping name            lastmsg address               qport \n");
			Com.Printf("--- ----- ---- --------------- ------- --------------------- ------\n");
			for (i = 0; i < SV_MAIN.maxclients.value_Renamed; i++)
			{
				cl = SV_INIT.svs.clients[i];
				if (0 == cl.state)
					continue;
				
				Com.Printf("%3i ", new Vargs().add(i));
				Com.Printf("%5i ", new Vargs().add(cl.edict.client.ps.stats[Defines.STAT_FRAGS]));
				
				if (cl.state == Defines.cs_connected)
					Com.Printf("CNCT ");
				else if (cl.state == Defines.cs_zombie)
					Com.Printf("ZMBI ");
				else
				{
					ping = cl.ping < 9999?cl.ping:9999;
					Com.Printf("%4i ", new Vargs().add(ping));
				}
				
				Com.Printf("%s", new Vargs().add(cl.name));
				l = 16 - cl.name.Length;
				for (j = 0; j < l; j++)
					Com.Printf(" ");
				
				Com.Printf("%7i ", new Vargs().add(SV_INIT.svs.realtime - cl.lastmessage));
				
				s = NET.AdrToString(cl.netchan.remote_address);
				Com.Printf(s);
				l = 22 - s.Length;
				for (j = 0; j < l; j++)
					Com.Printf(" ");
				
				Com.Printf("%5i", new Vargs().add(cl.netchan.qport));
				
				Com.Printf("\n");
			}
			Com.Printf("\n");
		}
		/*
		==================
		SV_ConSay_f
		==================
		*/
		public static void  SV_ConSay_f()
		{
			client_t client;
			int j;
			System.String p;
			System.String text; // char[1024];
			
			if (Cmd.Argc() < 2)
				return ;
			
			text = "console: ";
			p = Cmd.Args();
			
			if (p[0] == '"')
			{
				p = p.Substring(1, (p.Length - 1) - (1));
			}
			
			text += p;
			
			for (j = 0; j < SV_MAIN.maxclients.value_Renamed; j++)
			{
				client = SV_INIT.svs.clients[j];
				if (client.state != Defines.cs_spawned)
					continue;
				SV_SEND.SV_ClientPrintf(client, Defines.PRINT_CHAT, text + "\n");
			}
		}
		/*
		==================
		SV_Heartbeat_f
		==================
		*/
		public static void  SV_Heartbeat_f()
		{
			SV_INIT.svs.last_heartbeat = - 9999999;
		}
		/*
		===========
		SV_Serverinfo_f
		
		Examine or change the serverinfo string
		===========
		*/
		public static void  SV_Serverinfo_f()
		{
			Com.Printf("Server info settings:\n");
			Info.Print(Cvar.Serverinfo());
		}
		/*
		===========
		SV_DumpUser_f
		
		Examine all a users info strings
		===========
		*/
		public static void  SV_DumpUser_f()
		{
			if (Cmd.Argc() != 2)
			{
				Com.Printf("Usage: info <userid>\n");
				return ;
			}
			
			if (!SV_SetPlayer())
				return ;
			
			Com.Printf("userinfo\n");
			Com.Printf("--------\n");
			Info.Print(SV_MAIN.sv_client.userinfo);
		}
		/*
		==============
		SV_ServerRecord_f
		
		Begins server demo recording.  Every entity and every message will be
		recorded, but no playerinfo will be stored.  Primarily for demo merging.
		==============
		*/
		public static void  SV_ServerRecord_f()
		{
			//char	name[MAX_OSPATH];
			System.String name;
			sbyte[] buf_data = new sbyte[32768];
			sizebuf_t buf = new sizebuf_t();
			int len;
			int i;
			
			if (Cmd.Argc() != 2)
			{
				Com.Printf("serverrecord <demoname>\n");
				return ;
			}
			
			if (SV_INIT.svs.demofile != null)
			{
				Com.Printf("Already recording.\n");
				return ;
			}
			
			if (SV_INIT.sv.state != Defines.ss_game)
			{
				Com.Printf("You must be in a level to record.\n");
				return ;
			}
			
			//
			// open the demo file
			//
			name = FS.Gamedir() + "/demos/" + Cmd.Argv(1) + ".dm2";
			
			Com.Printf("recording to " + name + ".\n");
			FS.CreatePath(name);
			try
			{
				SV_INIT.svs.demofile = SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(name, "rw");
			}
			catch (System.Exception e)
			{
				Com.Printf("ERROR: couldn't open.\n");
				return ;
			}
			
			// setup a buffer to catch all multicasts
			SZ.Init(SV_INIT.svs.demo_multicast, SV_INIT.svs.demo_multicast_buf, SV_INIT.svs.demo_multicast_buf.Length);
			
			//
			// write a single giant fake message with all the startup info
			//
			SZ.Init(buf, buf_data, buf_data.Length);
			
			//
			// serverdata needs to go over for all types of servers
			// to make sure the protocol is right, and to set the gamedir
			//
			// send the serverdata
			MSG.WriteByte(buf, Defines.svc_serverdata);
			MSG.WriteLong(buf, Defines.PROTOCOL_VERSION);
			MSG.WriteLong(buf, SV_INIT.svs.spawncount);
			// 2 means server demo
			MSG.WriteByte(buf, 2); // demos are always attract loops
			MSG.WriteString(buf, Cvar.VariableString("gamedir"));
			MSG.WriteShort(buf, - 1);
			// send full levelname
			MSG.WriteString(buf, SV_INIT.sv.configstrings[Defines.CS_NAME]);
			
			for (i = 0; i < Defines.MAX_CONFIGSTRINGS; i++)
				if (SV_INIT.sv.configstrings[i].Length == 0)
				{
					MSG.WriteByte(buf, Defines.svc_configstring);
					MSG.WriteShort(buf, i);
					MSG.WriteString(buf, SV_INIT.sv.configstrings[i]);
				}
			
			// write it to the demo file
			Com.DPrintf("signon message length: " + buf.cursize + "\n");
			len = EndianHandler.swapInt(buf.cursize);
			//fwrite(len, 4, 1, svs.demofile);
			//fwrite(buf.data, buf.cursize, 1, svs.demofile);
			try
			{
				System.IO.BinaryWriter temp_BinaryWriter;
				temp_BinaryWriter = new System.IO.BinaryWriter(SV_INIT.svs.demofile);
				temp_BinaryWriter.Write((System.Int32) len);
				SV_INIT.svs.demofile.Write(SupportClass.ToByteArray(buf.data), 0, buf.cursize);
			}
			catch (System.IO.IOException e1)
			{
				// TODO: do quake2 error handling!
				SupportClass.WriteStackTrace(e1, Console.Error);
			}
			
			// the rest of the demo file will be individual frames
		}
		/*
		==============
		SV_ServerStop_f
		
		Ends server demo recording
		==============
		*/
		public static void  SV_ServerStop_f()
		{
			if (SV_INIT.svs.demofile == null)
			{
				Com.Printf("Not doing a serverrecord.\n");
				return ;
			}
			try
			{
				SV_INIT.svs.demofile.Close();
			}
			catch (System.IO.IOException e)
			{
				SupportClass.WriteStackTrace(e, Console.Error);
			}
			SV_INIT.svs.demofile = null;
			Com.Printf("Recording completed.\n");
		}
		/*
		===============
		SV_KillServer_f
		
		Kick everyone off, possibly in preparation for a new game
		
		===============
		*/
		public static void  SV_KillServer_f()
		{
			if (!SV_INIT.svs.initialized)
				return ;
			SV_MAIN.SV_Shutdown("Server was killed.\n", false);
			NET.Config(false); // close network sockets
		}
		/*
		===============
		SV_ServerCommand_f
		
		Let the game dll handle a command
		===============
		*/
		public static void  SV_ServerCommand_f()
		{
			
			GameSVCmds.ServerCommand();
		}
		//===========================================================
		
		/*
		==================
		SV_InitOperatorCommands
		==================
		*/
		public static void  SV_InitOperatorCommands()
		{
			Cmd.AddCommand("heartbeat", new AnonymousClassxcommand_t());
			Cmd.AddCommand("kick", new AnonymousClassxcommand_t1());
			Cmd.AddCommand("status", new AnonymousClassxcommand_t2());
			Cmd.AddCommand("serverinfo", new AnonymousClassxcommand_t3());
			Cmd.AddCommand("dumpuser", new AnonymousClassxcommand_t4());
			
			Cmd.AddCommand("map", new AnonymousClassxcommand_t5());
			Cmd.AddCommand("demomap", new AnonymousClassxcommand_t6());
			Cmd.AddCommand("gamemap", new AnonymousClassxcommand_t7());
			Cmd.AddCommand("setmaster", new AnonymousClassxcommand_t8());
			
			if (Globals.dedicated.value_Renamed != 0)
				Cmd.AddCommand("say", new AnonymousClassxcommand_t9());
			
			Cmd.AddCommand("serverrecord", new AnonymousClassxcommand_t10());
			Cmd.AddCommand("serverstop", new AnonymousClassxcommand_t11());
			
			Cmd.AddCommand("save", new AnonymousClassxcommand_t12());
			Cmd.AddCommand("load", new AnonymousClassxcommand_t13());
			
			Cmd.AddCommand("killserver", new AnonymousClassxcommand_t14());
			
			Cmd.AddCommand("sv", new AnonymousClassxcommand_t15());
		}
	}
}